#include <iostream>
using namespace std;
#include <string>
#include <vector>
#include <functional>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp" //.hpp是C++中一种头文件，允许将头文件和源文件的实现写在一块
#include "Channel.hpp"
// typedef function<void()> work_t;  //等价与下面
using work_t = function<void()>;
enum
{
    OK = 0,
    UsageError,
    PipeError,
    ForkError
};

class ProcessPool
{
public:
    ProcessPool(int n, work_t w)
        : processnum(n), work(w)
    {
    }
    // channels:输出型参数
    // work_t wotk:回调方法
    int InitProcessPool()
    {
        for (int i = 0; i < processnum; i++)
        {
            // 创建管道
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
                return PipeError;
            // 创建指定个数个进程
            int id = fork();
            if (id < 0)
                return ForkError;
            // 建立通信信道
            if (id == 0)
            {
                // child
                ::close(pipefd[1]);
                dup2(pipefd[0], 0); // 输入重定向
                work();
                ::close(pipefd[0]);
                ::exit(0);
            }
            // parent
            ::close(pipefd[0]);
            channels.emplace_back(pipefd[1], id);
            // Channel ch(pipefd[1], id);
            // channels.push_back(ch);
        }
        return OK;
    }
    void DistatchTask()
    {
        int who = 0;
        int num = 20;
        while (num--)
        {
            // a.选择一个任务，一个整数
            int task = tmp.SelectTask();
            // b.选择一个子进程channel
            Channel &cur = channels[who++];
            who %= channels.size();

            cout << "----------------------" << endl;
            cout << "send task:" << task << " to " << cur.Name() << ", 任务还剩：" << num << endl;
            cout << "----------------------" << endl;
            // c.派发任务
            cur.Send(task);

            sleep(1);
            cout << endl;
        }
    }
    void CleanProcessPool()
    {
        // version 1
        for (auto &c : channels)
        {
            c.Close();
        }
        for (auto &c : channels)
        {
            pid_t rid = waitpid(c.Id(), nullptr, 0);
            if (rid > 0)
            {
                cout << "child " << rid << " wait ... success" << endl;
            }
        }

        // version 2
        //  for (auto &c : channels)
        //  {
        //      c.Close();
        //      pid_t rid = waitpid(c.Id(), nullptr, 0);
        //      if (rid > 0)
        //      {
        //          cout << "child " << rid << " wait ... success" << endl;
        //      }
        //  }
        //  上面这样写为什么不对？
        //  在创建管道时，父进程的文件描述符表依次增大，创建的子进程会继承父进程的文件描述符表（也包括了管道的写端），
        //  导致每个管道的写端不只有父进程指向，子进程的文件描述符表也会指向！所以管道的写端没有关闭！
        //  如果想这样写，需要倒着关闭管道以及进程！

        // version 3
        // for (int i = channels.size() - 1; i >= 0; i--)
        // {
        //     channels[i].Close();
        //     pid_t rid = waitpid(channels[i].Id(), nullptr, 0);
        //     if (rid > 0)
        //     {
        //         cout << "child " << rid << " wait ... success" << endl;
        //     }
        // }

        // // version 4
        //  for (auto &c : channels)
        //  {
        //      c.Close();
        //      pid_t rid = waitpid(c.Id(), nullptr, 0);
        //      if (rid > 0)
        //      {
        //          cout << "child " << rid << " wait ... success" << endl;
        //      }
        //  }
        // 需要再创建管道时遍历channels，关闭历史写端
    }

    void DebugPrint()
    {
        for (auto c : channels)
            cout << c.Name() << endl;
    }

private:
    vector<Channel> channels;
    int processnum;
    work_t work;
};
